import uuid
from typing import ClassVar

from django.contrib.auth.models import AbstractUser, UserManager
from django.db import models
from django.db.models.signals import pre_save
from django.dispatch import receiver
from phonenumber_field.modelfields import PhoneNumberField


class User(AbstractUser):
    objects: ClassVar[UserManager] = UserManager()

    class Meta:
        verbose_name = "пользователь"
        verbose_name_plural = "пользователи"


class Client(models.Model):
    first_name = models.CharField("Имя")
    last_name = models.CharField("Фамилия")
    phone_number = PhoneNumberField("Номер телефона")
    email = models.EmailField("Почта", max_length=254, blank=True)
    note = models.TextField("Заметка", blank=True, null=True)

    created_on = models.DateTimeField("Дата добавления", auto_now_add=True)
    updated_on = models.DateTimeField(
        "Дата последнего изменения", auto_now=True
    )

    class Meta:
        verbose_name = "клиент"
        verbose_name_plural = "клиенты"
        constraints = [
            models.UniqueConstraint(
                fields=["first_name", "last_name", "phone_number"],
                name="unique_client",
            )
        ]

    def __str__(self) -> str:
        return f"#{self.id}: {self.first_name} {self.last_name}"


class ReqServices(models.Model):
    name = models.CharField("Название", unique=True)
    display_name = models.CharField("Отображаемое название")

    created_on = models.DateTimeField("Дата создания", auto_now_add=True)
    updated_on = models.DateTimeField(
        "Дата последнего изменения", auto_now=True
    )

    def __str__(self) -> str:
        return f"#{self.id}: {self.name}"

    class Meta:
        verbose_name = "источник"
        verbose_name_plural = "источники"


class Request(models.Model):
    class RequestStatus(models.IntegerChoices):
        NEW = 0, "Новая"
        RAW_SCHEME = 1, "Первичный чертеж"
        ON_MEASURE = 2, "Замер"
        FINALLY_SCHEME = 3, "Окончательный чертеж"
        COORDINATION = 4, "Согласование"
        ORDER = 5, "Заказ сформирован"
        LOST = 7, "Сорвалась"
        COMPLETED = 9, "Завершена"
        FAKE = 8, "Не настоящая"

    first_name = models.CharField("Имя", null=True)
    phone_number = PhoneNumberField("Номер телефона", null=True)
    number_in_program = models.CharField(
        "Номер в программе", blank=True, null=True
    )
    note = models.TextField("Заметка", blank=True, null=True)
    changes_history = models.TextField(
        "История изменений", blank=True, null=True
    )
    status = models.PositiveSmallIntegerField(
        "Статус", choices=RequestStatus.choices, default=RequestStatus.NEW
    )

    measure_sheet = models.JSONField("Замерочный лист", blank=True, null=True)

    client = models.ForeignKey(
        Client,
        on_delete=models.SET_NULL,
        related_name="requests",
        blank=True,
        null=True,
    )
    request_service = models.ForeignKey(
        ReqServices,
        on_delete=models.SET_DEFAULT,
        default=1,
        related_name="requests",
    )
    created_on = models.DateTimeField("Дата поступления", auto_now_add=True)
    updated_on = models.DateTimeField(
        "Дата последнего изменения", auto_now=True
    )

    def __str__(self) -> str:
        return f"#{self.id}: {self.first_name}"

    class Meta:
        verbose_name = "заявка"
        verbose_name_plural = "заявки"


@receiver(pre_save, sender=Request)
def update_client_info(sender, instance, **_):
    if instance.pk is not None:
        orig = sender.objects.get(pk=instance.pk)
        if orig.client != instance.client and instance.client is not None:
            instance.first_name = instance.client.first_name
            instance.phone_number = instance.client.phone_number


class Order(models.Model):
    class OrderStatus(models.IntegerChoices):
        NEW = 0, "Создан"
        SENT_TO_FABRIC = 1, "Отправлен на фабрику"
        MANUFACTURING = 2, "Изготавливается"
        DELIVERY = 3, "Передан в доставку"
        DELIVERED = 4, "Доставлен"
        ASSEMBLY = 5, "Сборка"
        COMPLETED = 9, "Завершен"

    number_in_program = models.PositiveIntegerField("Номер в программе")
    send_on_fabric_date = models.DateField("Дата отправки на фабрику")
    manufacturing_date = models.DateField("Дата изготовления")
    delivery_date = models.DateField("Дата доставки")
    status = models.PositiveSmallIntegerField(
        "Статус", choices=OrderStatus.choices, default=OrderStatus.NEW
    )
    note = models.TextField("Заметка", blank=True, null=True)

    created_on = models.DateTimeField("Дата создания", auto_now_add=True)
    updated_on = models.DateTimeField(
        "Дата последнего изменения", auto_now=True
    )

    client = models.ForeignKey(
        Client,
        on_delete=models.PROTECT,
        related_name="orders",
    )

    class Meta:
        verbose_name = "заказ"
        verbose_name_plural = "заказы"


class Contract(models.Model):
    number = models.CharField("Номер договора", unique=True)
    date = models.DateField("Дата заключения")
    amount_due = models.DecimalField(
        "Сумма к оплате", max_digits=10, decimal_places=2
    )
    amount_paid = models.DecimalField(
        "Сумма оплаченная", max_digits=10, decimal_places=2
    )
    note = models.TextField("Заметка", blank=True, null=True)

    created_on = models.DateTimeField("Дата создания", auto_now_add=True)
    updated_on = models.DateTimeField(
        "Дата последнего изменения", auto_now=True
    )

    order = models.ForeignKey(
        Order, on_delete=models.PROTECT, related_name="contracts"
    )
    client = models.ForeignKey(
        Client,
        on_delete=models.PROTECT,
        related_name="contracts",
    )

    class Meta:
        verbose_name = "договор"
        verbose_name_plural = "договоры"


class Warranty(models.Model):
    name = models.CharField("Название")
    end_date = models.DateField("Дата окончания")
    note = models.TextField("Заметка", blank=True, null=True)

    created_on = models.DateTimeField("Дата создания", auto_now_add=True)
    updated_on = models.DateTimeField(
        "Дата последнего изменения", auto_now=True
    )

    contract = models.ForeignKey(
        Contract, on_delete=models.PROTECT, related_name="warranties"
    )

    class Meta:
        verbose_name = "гарантия"
        verbose_name_plural = "гарантии"


class Reclamation(models.Model):
    number_in_program = models.PositiveIntegerField("Номер в программе")
    discovered_on = models.DateField("Дата обнаружения")
    remediation_date = models.DateField(
        "Дата устранения", null=True, blank=True
    )
    note = models.TextField("Заметка", blank=True, null=True)

    created_on = models.DateTimeField("Дата создания", auto_now_add=True)
    updated_on = models.DateTimeField(
        "Дата последнего изменения", auto_now=True
    )

    warranty = models.ForeignKey(
        Warranty, on_delete=models.PROTECT, related_name="reclamations"
    )
    contract = models.ForeignKey(
        Contract, on_delete=models.PROTECT, related_name="reclamations"
    )

    class Meta:
        verbose_name = "рекламация"
        verbose_name_plural = "рекламации"


class Worker(models.Model):
    first_name = models.CharField("Имя")
    last_name = models.CharField("Фамилия")
    phone_number = PhoneNumberField("Номер телефона")
    position = models.CharField("Должность")
    contract_number = models.CharField("Номер договора", unique=True)
    pay_type = models.CharField("Тип оплаты", blank=True, null=True)
    passport = models.CharField("Паспорт", blank=True, null=True)
    note = models.TextField("Заметка", blank=True, null=True)

    created_on = models.DateTimeField("Дата добавления", auto_now_add=True)
    updated_on = models.DateTimeField(
        "Дата последнего изменения", auto_now=True
    )

    class Meta:
        verbose_name = "работник"
        verbose_name_plural = "работники"
        constraints = [
            models.UniqueConstraint(
                fields=["first_name", "last_name", "phone_number"],
                name="unique_worker",
            )
        ]

    def __str__(self) -> str:
        return f"#{self.id}: {self.first_name} {self.last_name}"


class Delivery(models.Model):
    class DeliveryStatus(models.IntegerChoices):
        NOT_AGREED = 0, "Не согласована"
        AGREED = 1, "Согласована"
        DELIVERED = 9, "Доставлено"

    address = models.CharField("Адрес доставки")
    date = models.DateField("Дата доставки")
    status = models.PositiveSmallIntegerField(
        "Статус",
        choices=DeliveryStatus.choices,
        default=DeliveryStatus.NOT_AGREED,
    )
    paid = models.BooleanField("Оплачено", default=False)
    claim = models.TextField("Претензия", blank=True, null=True)
    note = models.TextField("Заметка", blank=True, null=True)

    created_on = models.DateTimeField("Дата создания", auto_now_add=True)
    updated_on = models.DateTimeField(
        "Дата последнего изменения", auto_now=True
    )

    order = models.ForeignKey(
        Order, on_delete=models.PROTECT, related_name="delivery"
    )
    worker = models.ForeignKey(
        Worker, on_delete=models.PROTECT, related_name="deliveries"
    )

    class Meta:
        verbose_name = "доставка"
        verbose_name_plural = "доставки"


class Assembly(models.Model):
    class AssemblyStatus(models.IntegerChoices):
        NOT_AGREED = 0, "Не согласована"
        AGREED = 1, "Согласована"
        ASSEMBLED = 9, "Произведена"

    address = models.CharField("Адрес сборки")
    date = models.DateField("Дата сборки")
    end_date = models.DateField("Дата окончания сборки", blank=True, null=True)
    status = models.PositiveSmallIntegerField(
        "Статус",
        choices=AssemblyStatus.choices,
        default=AssemblyStatus.NOT_AGREED,
    )
    cost = models.DecimalField("Стоимость", max_digits=10, decimal_places=2)
    paid = models.BooleanField("Оплачено", default=False)
    claim = models.TextField("Претензия", blank=True, null=True)
    note = models.TextField("Заметка", blank=True, null=True)
    worker_note = models.TextField("Заметка сборщика", blank=True, null=True)

    created_on = models.DateTimeField("Дата создания", auto_now_add=True)
    updated_on = models.DateTimeField(
        "Дата последнего изменения", auto_now=True
    )

    order = models.ForeignKey(
        Order, on_delete=models.PROTECT, related_name="assembly"
    )
    worker = models.ForeignKey(
        Worker, on_delete=models.PROTECT, related_name="assemblies"
    )

    class Meta:
        verbose_name = "сборка"
        verbose_name_plural = "сборки"


class MeasureSheet(models.Model):
    class MeasureSheetStatus(models.IntegerChoices):
        NOT_AGREED = 0, "Не согласован"
        AGREED = 1, "Согласован"
        MEASURED = 9, "Произведен"

    date = models.DateField("Дата замера")
    address = models.CharField("Адрес замера")
    status = models.PositiveSmallIntegerField(
        "Статус",
        choices=MeasureSheetStatus.choices,
        default=MeasureSheetStatus.NOT_AGREED,
    )
    data = models.JSONField("Данные замерочного листа")
    note = models.TextField("Заметка", blank=True, null=True)

    created_on = models.DateTimeField("Дата создания", auto_now_add=True)
    updated_on = models.DateTimeField(
        "Дата последнего изменения", auto_now=True
    )

    request = models.ForeignKey(
        Request,
        on_delete=models.SET_NULL,
        related_name="measure_sheets",
        blank=True,
        null=True,
    )
    order = models.ForeignKey(
        Order,
        on_delete=models.SET_NULL,
        related_name="measure_sheets",
        blank=True,
        null=True,
    )
    worker = models.ForeignKey(
        Worker, on_delete=models.PROTECT, related_name="measure_sheets"
    )

    class Meta:
        verbose_name = "замерочный лист"
        verbose_name_plural = "замерочные листы"

    def __str__(self) -> str:
        return f"#{self.id}"


class Task(models.Model):
    class TaskStatus(models.IntegerChoices):
        NEW = 0, "Создана"
        IN_PROGRESS = 1, "В процессе"
        DECLINED = 8, "Отменена"
        COMPLETED = 9, "Завершена"

    title = models.CharField("Заголовок")
    description = models.TextField("Описание")
    status = models.PositiveSmallIntegerField(
        "Статус", choices=TaskStatus.choices, default=TaskStatus.NEW
    )
    deadline = models.DateTimeField("Срок выполнения")

    created_on = models.DateTimeField("Дата создания", auto_now_add=True)
    updated_on = models.DateTimeField(
        "Дата последнего изменения", auto_now=True
    )

    creator = models.ForeignKey(User, on_delete=models.PROTECT)
    executor = models.ForeignKey(
        User,
        on_delete=models.SET_NULL,
        related_name="tasks",
        null=True,
        blank=True,
    )
    request = models.ForeignKey(
        Request,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="tasks",
    )
    order = models.ForeignKey(
        Order,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="tasks",
    )

    class Meta:
        verbose_name = "задача"
        verbose_name_plural = "задачи"

    def __str__(self) -> str:
        return f"#{self.id}: {self.title}"


class AttachmentGroups(models.Model):
    name = models.CharField("Название группы", unique=True)
    description = models.TextField("Описание группы", blank=True, null=True)

    created_on = models.DateTimeField("Дата создания", auto_now_add=True)
    updated_on = models.DateTimeField(
        "Дата последнего изменения", auto_now=True
    )

    class Meta:
        verbose_name = "группа вложений"
        verbose_name_plural = "группы вложений"

    def __str__(self) -> str:
        return f"#{self.id}: {self.name}"


def update_filename(_, filename):
    filename = uuid.uuid3(uuid.NAMESPACE_DNS, filename).hex
    return f"uploads/{filename[:2]}/{filename[2:4]}/{filename}"


class Attachment(models.Model):
    path = models.FileField(upload_to=update_filename)
    original_name = models.CharField(
        "Оригинальное название файла", blank=True, null=True
    )
    content_type = models.CharField(
        "Тип контента в файле", blank=True, null=True
    )
    size = models.PositiveIntegerField("Размер файла")
    creator = models.ForeignKey(User, on_delete=models.PROTECT)
    group = models.ForeignKey(
        AttachmentGroups, on_delete=models.SET_NULL, blank=True, null=True
    )

    created_on = models.DateTimeField("Дата добавления", auto_now_add=True)
    updated_on = models.DateTimeField(
        "Дата последнего изменения", auto_now=True
    )

    request = models.ForeignKey(
        Request,
        on_delete=models.CASCADE,
        blank=True,
        null=True,
        related_name="attachs",
    )
    contract = models.ForeignKey(
        Contract,
        on_delete=models.CASCADE,
        blank=True,
        null=True,
        related_name="attachs",
    )
    warranty = models.ForeignKey(
        Warranty,
        on_delete=models.CASCADE,
        blank=True,
        null=True,
        related_name="attachs",
    )
    reclamation = models.ForeignKey(
        Reclamation,
        on_delete=models.CASCADE,
        blank=True,
        null=True,
        related_name="attachs",
    )
    delivery = models.ForeignKey(
        Delivery,
        on_delete=models.CASCADE,
        blank=True,
        null=True,
        related_name="attachs",
    )
    assembly = models.ForeignKey(
        Assembly,
        on_delete=models.CASCADE,
        blank=True,
        null=True,
        related_name="attachs",
    )
    worker = models.ForeignKey(
        Worker,
        on_delete=models.CASCADE,
        blank=True,
        null=True,
        related_name="attachs",
    )

    def get_attach_url(self):
        return f"/protected/storage/{self.id}"

    def __str__(self) -> str:
        return f"#{self.id}: {self.original_name}"

    # def save(self, *args, **kwargs):
    #     # Если файл был загружен
    #     if self.path:
    #         # Получаем размер файла
    #         self.size = self.path.size
    #         self.original_name = self.path.name
    #         self.content_type = self.path.file.content_type
    #
    #     super().save(*args, **kwargs)

    class Meta:
        verbose_name = "вложение"
        verbose_name_plural = "вложения"
